﻿
// 2018/8/2：首个记录
// 2018/11/29: 增加Protocols静态属性
// 2019/3/22: 时间戳赋值
// 2019/3/29: 添加注释
// 2019/6/17: 增加ArrowCombination
// 2019/12/17: 修正注释

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using ASEva;

namespace TrafficSampleV1
{
    enum TrafficElementType
    {
        Invalid,
        SpeedLimit, // 限速标志
        StopSign, // 停止标志
        TrafficLight, // 交通信号灯
        StopLine, // 停止线
        SpeedBump, // 减速带
        ArrowMark, // 路面箭头
        ProhibitionZone, // 禁停区
        ParkingSlot, // 停车位
        ZebraCrossing, // 斑马线
    }

    class ArrowCombination
    {
        public bool GoStraight { get; set; } // 直行
        public bool TurnLeft { get; set; } // 左转
        public bool TurnRight { get; set; } // 右转
        public bool UTurn { get; set; } // 调头
    };

    class TrafficElement // 交通元素基类
    {
        public TrafficElementType Type { get; set; } // 交通元素类别
    }

    class SpeedLimit : TrafficElement
    {
        public uint LimitedSpeed { get; set; } // 限速 KPH
        public double? PosX { get; set; } // 纵向位置 m
        public double? PosY { get; set; } // 横向位置 m

        public SpeedLimit()
        {
            Type = TrafficElementType.SpeedLimit;
        }
    }

    class StopSign : TrafficElement
    {
        public double? PosX { get; set; } // 纵向位置 m
        public double? PosY { get; set; } // 横向位置 m

        public StopSign()
        {
            Type = TrafficElementType.StopSign;
        }
    }

    enum TrafficLightState
    {
        Invalid,
        Stop, // 红灯
        Continue, // 黄灯
        Caution, // 黄灯闪烁
        Allow, // 绿灯
    }

    class TrafficLight : TrafficElement
    {
        public TrafficLightState State { get; set; } // 信号灯状态
        public ArrowCombination Arrows { get; set; } // 信号灯的箭头组合，若不分方向(实心圆信号)则为空
        public double? PosX { get; set; } // 纵向位置 m
        public double? PosY { get; set; } // 横向位置 m

        public TrafficLight()
        {
            Type = TrafficElementType.TrafficLight;
        }
    }

    class StopLine : TrafficElement
    {
        public double Distance { get; set; } // 停止线与x轴相交位置 m
        public double? Heading { get; set; } // 停止线法线朝向，单位°，逆时针为正方向

        public StopLine()
        {
            Type = TrafficElementType.StopLine;
        }
    }

    class SpeedBump : TrafficElement
    {
        public double Distance { get; set; } // 减速带与x轴相交位置 m
        public double? Heading { get; set; } // 减速带法线朝向，单位°，逆时针为正方向

        public SpeedBump()
        {
            Type = TrafficElementType.SpeedBump;
        }
    }

    class ArrowMark : TrafficElement
    {
        public ArrowCombination Arrows { get; set; } // 路面标志的箭头组合
        public double? PosX { get; set; } // 纵向位置 m
        public double? PosY { get; set; } // 横向位置 m

        public ArrowMark()
        {
            Type = TrafficElementType.ArrowMark;
        }
    }

    class ProhibitionZone : TrafficElement
    {
        public double CenterX { get; set; } // 禁停区中央的纵向位置 m
        public double CenterY { get; set; } // 禁停区中央的横向位置 m
        public double Width { get; set; } // 禁停区的宽度 m
        public double Length { get; set; } // 禁停区的长度 m
        public double? Heading { get; set; } // 禁停区的朝向(沿长度方向)，单位°，逆时针为正方向
        
        public ProhibitionZone()
        {
            Type = TrafficElementType.ProhibitionZone;
        }
    }

    class ParkingSlot : TrafficElement
    {
        public double CenterX { get; set; } // 停车位中央的纵向位置 m
        public double CenterY { get; set; } // 停车位中央的横向位置 m
        public double Width { get; set; } // 停车位的宽度 m
        public double Length { get; set; } // 停车位的长度 m
        public double? Heading { get; set; } // 停车位的朝向(沿长度方向)，单位°，逆时针为正方向

        public ParkingSlot()
        {
            Type = TrafficElementType.ParkingSlot;
        }
    }

    class ZebraCrossing : TrafficElement
    {
        public double NearDistance { get; set; } // 斑马线近端与x轴相交位置 m
        public double? FarDistance { get; set; } // 斑马线远端与x轴相交位置 m
        public double? Heading { get; set; } // 斑马线的朝向，单位°，逆时针为正方向

        public ZebraCrossing()
        {
            Type = TrafficElementType.ZebraCrossing;
        }
    }

    class TrafficSample : Sample
    {
        public List<TrafficElement> Elements { get; set; } // 交通元素列表

        public TrafficSample()
        {
            Elements = new List<TrafficElement>();
        }

        public override string GetGeneralSampleProtocol()
        {
            return "traffic-sample-v1";
        }

        public static string[] Protocols
        {
            get
            {
                return new string[] { "traffic-sample-v1" };
            }
        }
        public override string[] GetGeneralSampleProtocols()
        {
            return Protocols;
        }

        public override bool FromGeneralSample(GeneralSample sample)
        {
            if (sample.Protocol == "traffic-sample-v1")
            {
                var v = sample.Values.ToArray();
                if (v.Length < 1) return false;
                if (!v[0].IsNumber()) return false;

                int count = v[0].ToInt().Value;
                if (v.Length < 1 + 3 * count) return false;

                if (count > 0)
                {
                    int lastOffset = v[3 * count - 1].ToInt().Value;
                    int lastLength = v[3 * count].ToInt().Value;
                    int totalDataSize = lastOffset + lastLength;
                    if (v.Length < 1 + 3 * count + totalDataSize) return false;
                }

                var list = new List<TrafficElement>();
                for (int i = 0; i < count; i++)
                {
                    if (!v[1 + 3 * i].IsNumber() || !v[2 + 3 * i].IsNumber()) continue;

                    var type = (TrafficElementType)v[1 + 3 * i].ToInt().Value;
                    if (type == TrafficElementType.Invalid) continue;

                    int bas = 1 + 3 * count + v[2 + 3 * i].ToInt().Value;

                    TrafficElement newElem = null;
                    switch (type)
                    {
                        case TrafficElementType.SpeedLimit:
                            newElem = new SpeedLimit()
                            {
                                LimitedSpeed = (uint)v[bas].ToInt().Value,
                                PosX = v[bas + 1].ToDouble(),
                                PosY = v[bas + 2].ToDouble(),
                            };
                            break;
                        case TrafficElementType.StopSign:
                            newElem = new StopSign()
                            {
                                PosX = v[bas].ToDouble(),
                                PosY = v[bas + 1].ToDouble(),
                            };
                            break;
                        case TrafficElementType.TrafficLight:
                            var trafficLight = new TrafficLight()
                            {
                                State = (TrafficLightState)v[bas].ToInt().Value,
                                PosX = v[bas + 1].ToDouble(),
                                PosY = v[bas + 2].ToDouble(),
                            };
                            if (v[bas + 3].IsNumber() && v[bas + 4].IsNumber() && v[bas + 5].IsNumber() && v[bas + 6].IsNumber())
                            {
                                trafficLight.Arrows = new ArrowCombination()
                                {
                                    GoStraight = v[bas + 3].ToInt().Value == 1,
                                    TurnLeft = v[bas + 4].ToInt().Value == 1,
                                    TurnRight = v[bas + 5].ToInt().Value == 1,
                                    UTurn = v[bas + 6].ToInt().Value == 1,
                                };
                            }
                            newElem = trafficLight;
                            break;
                        case TrafficElementType.StopLine:
                            newElem = new StopLine()
                            {
                                Distance = v[bas].ToDouble().Value,
                                Heading = v[bas + 1].ToDouble(),
                            };
                            break;
                        case TrafficElementType.SpeedBump:
                            newElem = new SpeedBump()
                            {
                                Distance = v[bas].ToDouble().Value,
                                Heading = v[bas + 1].ToDouble(),
                            };
                            break;
                        case TrafficElementType.ArrowMark:
                            newElem = new ArrowMark()
                            {
                                Arrows = new ArrowCombination()
                                {
                                    GoStraight = v[bas].ToInt().Value == 1,
                                    TurnLeft = v[bas + 1].ToInt().Value == 1,
                                    TurnRight = v[bas + 2].ToInt().Value == 1,
                                    UTurn = v[bas + 3].ToInt().Value == 1,
                                },
                                PosX = v[bas + 4].ToDouble(),
                                PosY = v[bas + 5].ToDouble(),
                            };
                            break;
                        case TrafficElementType.ProhibitionZone:
                            newElem = new ProhibitionZone()
                            {
                                CenterX = v[bas].ToDouble().Value,
                                CenterY = v[bas + 1].ToDouble().Value,
                                Width = v[bas + 2].ToDouble().Value,
                                Length = v[bas + 3].ToDouble().Value,
                                Heading = v[bas + 4].ToDouble(),
                            };
                            break;
                        case TrafficElementType.ParkingSlot:
                            newElem = new ParkingSlot()
                            {
                                CenterX = v[bas].ToDouble().Value,
                                CenterY = v[bas + 1].ToDouble().Value,
                                Width = v[bas + 2].ToDouble().Value,
                                Length = v[bas + 3].ToDouble().Value,
                                Heading = v[bas + 4].ToDouble(),
                            };
                            break;
                        case TrafficElementType.ZebraCrossing:
                            newElem = new ZebraCrossing()
                            {
                                NearDistance = v[bas].ToDouble().Value,
                                FarDistance = v[bas + 1].ToDouble(),
                                Heading = v[bas + 2].ToDouble(),
                            };
                            break;
                    }
                    if (newElem != null) list.Add(newElem);
                }
                Elements = list;

                Base = sample.Base;
                Offset = sample.Offset;
                Timeline = sample.Timeline;

                return true;
            }
            return false;
        }
    }
}
